package com.demo6;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.Symbol;
import com.github.unidbg.arm.backend.HypervisorFactory;
import com.github.unidbg.arm.context.EditableArm32RegisterContext;
import com.github.unidbg.arm.context.RegisterContext;
import com.github.unidbg.debugger.BreakPointCallback;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.MemoryAllocBlock;
import com.github.unidbg.memory.MemoryBlock;
import com.github.unidbg.pointer.UnidbgPointer;
import com.github.unidbg.utils.Inspector;
import unicorn.ArmConst;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;


public class XG15 extends AbstractJni {

    private static AndroidEmulator createARMEmulator() {
        return AndroidEmulatorBuilder.for32Bit()
                .setProcessName("com.ss.android.ugc.aweme")
                .addBackendFactory(new HypervisorFactory(true))
                .build();
    }

    private AndroidEmulator emulator;
    private Module module;
    private VM vm;
    private final Memory memory;

    private XG15(){
        emulator = createARMEmulator();

        memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM();
        vm.setJni(this);
        vm.setVerbose(true);
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/demo6/libmetasec_ml.so"), false);
        module = dm.getModule();
        hookResult();
        hookMemcpy();
    }

    public void hookMemcpy(){
        // void *memcpy(void *str1, const void *str2, size_t n)
        final RegisterContext registerContext = emulator.getContext();
        Symbol memcpySymbol = module.findSymbolByName("memcpy");
        emulator.attach().addBreakPoint(memcpySymbol.getAddress(), new BreakPointCallback() {
            @Override
            public boolean onHit(Emulator<?> emulator, long address) {
                System.out.print("call memcpy");
                UnidbgPointer srcAddress = registerContext.getPointerArg(1);
                int length = registerContext.getIntArg(2);
                Inspector.inspect(srcAddress.getByteArray(0, length), "source:"+srcAddress);
                return true;
            }
        });
    }

    public void hookResult(){
        emulator.attach().addBreakPoint(module.base + 0x333B2, new BreakPointCallback() {
            @Override
            public boolean onHit(Emulator<?> emulator, long address) {
                EditableArm32RegisterContext registerContext = emulator.getContext();
                UnidbgPointer pointer = registerContext.getR2Pointer();
                Inspector.inspect(pointer.getByteArray(0, 52), "xg"+pointer);
                emulator.getBackend().emu_stop();
                return true;
            }
        });
    }


    private void callXG(){
        MemoryBlock partTwo = memory.malloc(0x10, false);
        MemoryBlock partTwoStr = memory.malloc(10, false);

        byte[] stub = hexStringToBytes("50 a1 44 f2 74 d6 20 3d 46 dc 81 e7 e8 9f 3b 89");
        partTwoStr.getPointer().write(0, stub, 0, stub.length);
        long field_0 = module.base + 0x990C8; // free_string
        int size = 0x20;
        int len = 0x10;
        long ptr = partTwoStr.getPointer().peer;
        partTwo.getPointer().write(0, new int[]{(int) field_0, size, len, (int) ptr}, 0, 4);

        // ================================================================================
        String params = "version_code=150700&device_platform=android&device_id=2463360436933182&aid=1128&iid=2806399523104615&tt_data=a";
        MemoryBlock param = memory.malloc(0x10, false);
        UnidbgPointer paramPointer = param.getPointer();
        MemoryBlock paramStr = memory.malloc(params.length() + 1, false);
        UnidbgPointer paramStrPointer = paramStr.getPointer();

        paramStrPointer.write(0, params.getBytes(StandardCharsets.UTF_8), 0, params.length());
        field_0 = module.base + 0x990C8; // free_string
        size = params.length() + 1;
        len = params.length();
        ptr = paramStrPointer.peer;
        paramPointer.write(0, new int[]{(int) field_0, size, len, (int) ptr}, 0, 4);

        // ================================================================================
        MemoryBlock param5 = memory.malloc(0x10, false);
        UnidbgPointer param5Pointer = param5.getPointer();

        MemoryBlock param6 = memory.malloc(0x10, false);
        UnidbgPointer param6Pointer = param6.getPointer();
        // ================================================================================
        MemoryBlock structXg = memory.malloc(0x10, false);
        UnidbgPointer structXgPointer = structXg.getPointer();

        byte[] structXG = hexStringToBytes("cf 2a 9f 61 00 00 00 00");
        structXgPointer.write(0, structXG, 0, structXG.length);
        structXgPointer.write(8, new int[]{(int) partTwo.getPointer().peer, (int) paramPointer.peer,
                (int) param5Pointer.peer, (int) param6Pointer.peer}, 0, 0x4);


        module.callFunction(emulator, 0x32F10 + 1, structXgPointer);
    }

    public static void main(String[] args) throws Exception {
        XG15 xg15 = new XG15();
        xg15.callXG();
    }

    public static byte[] hexStringToBytes(String hexString) {
        hexString = hexString.replace(" ", "");
        hexString = hexString.length() % 2 != 0 ? "0" + hexString : hexString;

        byte[] b = new byte[hexString.length() / 2];
        for (int i = 0; i < b.length; i++) {
            int index = i * 2;
            int v = Integer.parseInt(hexString.substring(index, index + 2), 16);
            b[i] = (byte) v;
        }
        return b;
    }
}


